(function (fruit, dom, util, $, global, document, undefined) {
    var rDRAG = /^DRAG/;
    var registeredMap = {};
    var elementMap = {};
    var eventObject = {
        getEventDoc:function (inEvent) {
            var target = this.getTarget(inEvent);
            return target.ownerDocument || document;
        },
        getPageX:function (inEvent) {
            var doc = this.getEventDoc(inEvent);
            return inEvent.pageX || (inEvent.clientX + (doc.documentElement.scrollLeft || doc.body.scrollLeft) - 2) || 0;
        },
        getPageY:function (inEvent) {
            var doc = this.getEventDoc(inEvent);
            return inEvent.pageY || (inEvent.clientY + (doc.documentElement.scrollTop || doc.body.scrollTop) - 2) || 0;
        },
        getDetail:function (inEvent) {
            return inEvent.detail || -(inEvent.wheelDelta || 0);
        },
        getKeyCode:function (inEvent) {
            return ('keyCode' in inEvent) ? inEvent.keyCode : (inEvent.charCode || inEvent.which || 0);
        },
        getTarget:function (inEvent) {
            var target = inEvent.target || inEvent.srcElement || document;//thanks jquery
            //Mousewheel will apeared
            if (target.nodeType === 3) {
                target = target.parentNode;
            }
            return target;
        },
        getRelatedTarget:function (inEvent) {
            var related;
            if ("relatedTarget" in inEvent) {
                related = inEvent.relatedTarget
            } else {
                if ('mouseover' == inEvent.type) {
                    related = inEvent.fromElement;
                } else {
                    related = inEvent.toElement;
                }
            }
            return related
        },
        stopPropagation:function (inEvent) {
            if ('stopPropagation' in inEvent) {
                inEvent.stopPropagation();
            } else {
                inEvent.cancelBubble = true;
            }
        },
        preventDefault:function (inEvent) {
            if ('preventDefault' in inEvent) {
                inEvent.preventDefault();
            } else {
                inEvent.returnValue = false;
            }
        },
        getKeyChar:function (inEvent) {
            return String.fromCharCode(inEvent.keyCode);
        },
        hasSystemKey:function (inEvent) {
            return (inEvent.ctrlKey || inEvent.altKey);
        }
    };

    function normalizeEvent(inEvent) {
        var $event = eventObject;
        return {
            originalEvent:inEvent,
            eventDoc:$event.getEventDoc(inEvent),
            type:inEvent.type,
            pageX:$event.getPageX(inEvent),
            pageY:$event.getPageY(inEvent),
            detail:$event.getDetail(inEvent),
            keyCode:$event.getKeyCode(inEvent),
            target:$event.getTarget(inEvent),
            relatedTarget:$event.getRelatedTarget(inEvent),
            keyChar:$event.getRelatedTarget(inEvent),
            hasSystemKey:$event.hasSystemKey(inEvent),
            stopPropagation:function () {
                $event.stopPropagation(inEvent);
            },
            preventDefault:function () {
                $event.preventDefault(inEvent);
            }
        };
    }

    fruit.define('fruit.event.DefaultOriginalProcessor', {
        methods:{
            isTarget:function (inIdentity, inEvent) {
                var target = inEvent.target;
                return inIdentity == dom.identity(target) || engine.contains(inIdentity, target);
            },
            process:function (inEvent, inProcessor) {
                engine.trigger(inEvent.type, inEvent, inProcessor);
            },
            normalize:function (inEvent) {
                return normalizeEvent(inEvent);
            }
        }
    });
    fruit.define('fruit.event.DefaultSimulateProcessor', fruit.event.DefaultOriginalProcessor, {
        methods:{
            process:function (simType, srcType, processor, capture) {
                engine.addEventListener(document, srcType, function (event) {
//                    engine.trigger(simType, processor.normalize(event), processor);
                    engine.trigger(simType, event, processor);
                }, capture);
            }
        }
    });
    var engine = fruit.event.engine = {
        eventMap:{},
        contains:function (inIdentity, inElement) {
            if (!inElement) {
                return false;
            }
            var parent = dom.parent(inElement);
            if (parent) {
                if (parent.$id == inIdentity) {
                    return true;
                }
            }
            return engine.contains(inIdentity, parent);
        },
        ready:function (fn) {
            return $(document).ready(fn);
        },
        addEventListener:(function () {
            if (document.addEventListener) {
                return function (el, type, fn, capture) {
                    el.addEventListener(type, function (inEvent) {
                        fn.call(el, normalizeEvent(inEvent))
                    }, capture || false);
                };
            } else {
                return function (el, type, fn, capture) {
                    el.attachEvent("on" + type, function () {
                        fn.call(el, normalizeEvent(global.event));
                    });
                };
            }
        }()),
        removeEventListener:(function () {
            if (document.removeEventListener) {
                return function (el, type, fn, capture) {
                    el.removeEventListener(type, function (inEvent) {
                        fn.call(el, normalizeEvent(inEvent))
                    }, capture || false);
                };
            } else {
                return function (el, type, fn, capture) {
                    el.detachEvent("on" + type, function () {
                        fn.call(el, normalizeEvent(global.event));
                    });
                };
            }
        }()),
        on:function (inElement, inType, inFn, inContext, inData) {
            var eventMap = engine.eventMap[inType] || {};
            var $id = dom.identity(inElement);
            elementMap[$id] = inElement;
            eventMap[$id] = eventMap[$id] || [null];
            eventMap[$id].push({
                fn:inFn,
                context:inContext,
                data:inData
            });
            engine.eventMap[inType] = eventMap;
            engine.registerEventByType(inType);
        },
        getById:function (inId) {
            return elementMap[inId];
        },
        off:function (inElement, inType, inFn, inContext) {
            if (!inElement) {
                return;
            }
            var eventMap = engine.eventMap[inType];
            var $id = dom.identity(inElement);
            if (eventMap[$id]) {
                if (typeof inFn !== 'undefined') {
                    util.each(eventMap[$id], function (val, index) {
                        if (inFn === val.fn) {
                            if (typeof inContext !== 'undefined' && inContext === val.context) {
                                return eventMap.splice(index, 1), false;
                            }
                            return eventMap.splice(index, 1), false;
                        }
                    });
                } else {
                    eventMap[$id].length = 0;
                }
            }
        },
        one:function (inElement, inType, inFn, inContext, inData) {
            if (!inElement) {
                return;
            }
            var eventMap = engine.eventMap[inType] || {};
            var $id = dom.identity(inElement);
            elementMap[$id] = inElement;
            eventMap[$id] = [
                {
                    fn:inFn,
                    context:inContext,
                    data:inData
                }
            ];
            engine.eventMap[inType] = eventMap;
            engine.registerEventByType(inType);
        },
        once:function (inElement, inType, inFn, inContext, inData) {
            engine.on(inElement, inType, inFn, inContext, inData);
            inFn.call(inContext, inData);
            engine.off(inElement, inType, inFn, inContext);
        },
        __findIdFromParent:function (inEventMap, inElement) {
            var $id = inElement.$id;
            var parentNode = inElement.parentNode;
            if ($id && $id in inEventMap) {
                return $id;
            } else {
                return parentNode && engine.__findIdFromParent(inEventMap, parentNode);
            }
        },
        trigger:function (inType, inEvent, inProcessor) {
            var eventMap = engine.eventMap[inType],
                context, self = this, element,
                eventItem,$id;
            if (!eventMap) {
                return;
            }
            $id = engine.__findIdFromParent(eventMap, inEvent.target);
            eventItem=eventMap[$id];
            util.each(eventItem, function (val) {
                if (val !== null && inProcessor.isTarget($id, inEvent)) {
                    element = engine.getById($id);
                    if (element && element.getAttribute && !!element.getAttribute("disabled")) {
                        return;
                    }
                    context = val.context || self;
                    val.fn.call(context, val.data, inEvent);
                }
            });
            /*util.each(eventMap, function (eventItem, $id) {

             });*/
        },
        hasEvent:function (inEvent) {
            return inEvent in registeredMap;
        },
        registerEventByType:function (type) {
            var map = registeredMap[type];
            if (map.simulate) {
                engine.simulateEvent(type, map.simulate, map.processor);
            } else {
                engine.registerEvent(type, map.processor);
            }
        },
        registerEvent:function (inSrcType, inProcessor) {
            var processor;
            if (!registeredMap[inSrcType].active) {
                processor = new (inProcessor || fruit.event.DefaultOriginalProcessor)();
                registeredMap[inSrcType] = {
                    processor:processor,
                    active:true
                };
                engine.addEventListener(processor.target || document, inSrcType, function (event) {
                    processor.process(processor.normalize(event), processor);
                });
            }
        },
        simulateEvent:function (inSimType, inSrcType, inProcessor) {
            var processor;
            if (!registeredMap[inSimType].active) {
                if (!rDRAG.test(inSimType)) {
                    processor = new (inProcessor || fruit.event.DefaultSimulateProcessor)();
                } else {
                    processor = inProcessor;
                    processor.initData && processor.initData(registeredMap[inSimType].data);
                }
                registeredMap[inSimType] = {
                    processor:processor,
                    active:true
                };
                processor.process(inSimType, inSrcType, processor);
            }
        },
        registerProcessor:function (inType, inSimulate, inProcessor, inActive, inData) {
            if ('string' === typeof inType) {
                registeredMap[inType] = registeredMap[inType] || {};
                inProcessor && (registeredMap[inType]['processor'] = inProcessor);
                inSimulate && (registeredMap[inType]['simulate'] = inSimulate);
                inActive && (registeredMap[inType]['active'] = inActive);
                inData && (registeredMap[inType]['data'] = inData);
            } else {
                util.each(inType, function (type, key) {
                    engine.registerProcessor(type, inSimulate[key], inProcessor, inActive, inData);
                });
            }
        },
        registerProcessors:function (inEventTypes, isInterface) {
            var i, eventName, length = inEventTypes.length;
            var interfaceEvent;
            for (i = 0; i < length; i++) {
                eventName = inEventTypes[i];
                interfaceEvent = !isInterface ? eventName.toLowerCase() : isInterface;
                engine.registerProcessor(eventName, interfaceEvent);
            }
        },
        unregisterProcessor:function (inType) {
            delete registeredMap[inType];
        }
    };
    var standardEventTypes = ["mousedown", "mousemove", "mouseup", "mouseover", "mouseout", "click", "dblclick", "keydown", "keyup", "keypress", "cut", "copy", "select", "contextmenu", "dragstart", "drag", "dragend", "focus", "blur", "change", "input", "mousewheel"];
    var standardTouchEventTypes = ["touchstart", "touchmove", "touchend"];
    var interfaceStandardEvents = ["MOUSEDOWN", "MOUSEMOVE", "MOUSEUP", "MOUSEOVER", "MOUSEOUT", "CLICK", "DBLCLICK", "KEYDOWN", "KEYUP", "KEYPRESS"];
    engine.registerProcessors(standardEventTypes);
    engine.registerProcessors(standardTouchEventTypes);
    //TODO:
//    engine.registerProcessors(interfaceStandardEvents, true);
    for (var i = 0; i < interfaceStandardEvents.length; i++) {
        engine.registerProcessor(interfaceStandardEvents[i], interfaceStandardEvents[i].toLocaleLowerCase());
    }
    fruit.ready = engine.ready;
    fruit.event.engine.registeredMap = registeredMap;
}(fruit, fruit.dom, fruit.util, jQuery, window, document, undefined));